%%%---------------------------------------------------------------------------------------
%%% @author    Stuart Jackson <sjackson@simpleenigma.com> [http://erlsoft.org]
%%% @copyright 2006 - 2008 Simple Enigma, Inc. All Rights Reserved.
%%% @doc       Elestic Cloud Computing Library
%%% @reference See <a href="http://erlsoft.org/modules/eifaws" target="_top">Erlang Software Framework</a> for more information
%%% @reference See <a href="http://eifaws.googlecode.com" target="_top">ErlMail Google Code Repository</a> for more information
%%% @version   0.0.1
%%% @since     0.0.1
%%% @end
%%%
%%%
%%% The MIT License
%%%
%%% Copyright (c) 2008 Stuart Jackson, Simple Enigma, Inc. All Righs Reserved
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
%%% of this software and associated documentation files (the "Software"), to deal
%%% in the Software without restriction, including without limitation the rights
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%%% copies of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%%% THE SOFTWARE.
%%%
%%%
%%%---------------------------------------------------------------------------------------

-module(eifaws_ec2).
-author('sjackson@simpleenigma.com').
-include("../include/eifaws.hrl").
-include_lib("xmerl/include/xmerl.hrl").

-export([command/2]).
-export([deregister_image/2,describe_images/4,register_image/2]).
-export([confirm_product_instance/3,describe_instances/1,describe_instances/2,run_instances/2,terminate_instances/2,reboot_instances/2]).
-export([create_key_pair/2,delete_key_pair/2,describe_key_pairs/1,describe_key_pairs/2]).
-export([describe_image_attribute/3,modify_image_attribute/2,reset_image_attribute/3]).
-export([attach_volume/4,create_snapshot/2,create_volume/3,delete_snapshot/2,delete_volume/2,detach_volume/2,describe_snapshots/2,describe_volumes/2]).
-export([authorize_security_group_ingress/4,authorize_security_group_ingress/6,create_security_group/3,delete_security_group/2,describe_security_groups/2,revoke_security_group_ingress/4,revoke_security_group_ingress/6]).
-export([allocate_address/1,associate_address/3,describe_addresses/2,disassociate_address/2,release_address/2]).
-export([describe_availability_zones/2]).
-export([get_console_output/2]).

-export([meta_data/1,meta_data/2]).
-export([build_run_instances/1,build_list/2,build_list/3]).
-export([query_sort/1,build_query/2]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% EC2 API Funcations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%-------------------------------------------------------------------------
%% @spec (AWSInfo) -> any()
%% @doc  Acquires an elastic IP address for use with your account.
%% @end
%%-------------------------------------------------------------------------
allocate_address(AWSInfo) ->
	Query = [{'Action',"AllocateAddress"}],
	command(AWSInfo,Query).
%%-------------------------------------------------------------------------
%% @spec (AWSInfo,InstanceId,PublicIp) -> any()
%% @doc  Associates an elastic IP address with an instance.
%% @end
%%-------------------------------------------------------------------------
associate_address(AWSInfo,InstanceId,PublicIp) ->
	Query = [{'Action',"AssociateAddress"},{'InstanceId',InstanceId},{'PublicIp',PublicIp}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId,InstanceId,Device) -> any()
%% @doc  Attaches an Amazon EBS volume to an instance.
%% @end
%%-------------------------------------------------------------------------
attach_volume(AWSInfo,VolumeId,InstanceId,Device) ->
	Query = [{'Action',"AttachVolume"},{'VolumeId',VolumeId},{'InstanceId',InstanceId},{'Device',Device}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName,SourcesSecurityGroupName,SourcesSecurityGroupOwnerId) -> any()
%% @doc  Adds permissions to a security group.
%% @end
%%-------------------------------------------------------------------------
authorize_security_group_ingress(AWSInfo,GroupName,SourceSecurityGroupName,SourceSecurityGroupOwnerId) ->
	Query = [{'Action',"AuthorizeSecurityGroupIngress"},{'GroupName',GroupName},{'SourceSecurityGroupName',SourceSecurityGroupName},{'SourceSecurityGroupOwnerId',SourceSecurityGroupOwnerId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName,IpProtocol,FromPort,ToPort,CidrIp) -> any()
%% @doc  Adds permissions to a security group.
%% @end
%%-------------------------------------------------------------------------
authorize_security_group_ingress(AWSInfo,GroupName,IpProtocol,FromPort,ToPort,CidrIp) ->
	Query = [{'Action',"AuthorizeSecurityGroupIngress"},{'GroupName',GroupName},{'IpProtocol',IpProtocol},{'FromPort',FromPort},{'ToPort',ToPort},{'CidrIp',CidrIp}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ProductCode,InstanceId) -> any()
%% @doc  Returns true if the specified product code is attached to the specified instance.
%% @end
%%-------------------------------------------------------------------------
confirm_product_instance(AWSInfo,ProductCode,InstanceId) ->
	Query = [{'Action',"ConfirmProductInstance"},{'InstanceId',InstanceId},{'ProductCode',ProductCode}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,KeyName) -> any()
%% @doc  Creates a new 2048 bit RSA keypair and returns a unique ID that can be used to reference this keypair when launching new instances.
%% @end
%%-------------------------------------------------------------------------
create_key_pair(AWSInfo,KeyName) ->
	Query = [{'Action',"CreateKeyPair"},{'KeyName',KeyName}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName,GroupDescription) -> any()
%% @doc  Creates a new security group.
%% @end
%%-------------------------------------------------------------------------
create_security_group(AWSInfo,GroupName,GroupDescription) ->
	Query = [{'Action',"CreateSecurityGroup"},{'GroupName',GroupName},{'GroupDescription',GroupDescription}],
	command(AWSInfo,Query).
%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId) -> any()
%% @doc  Creates a snapshot of an Amazon EBS volume and stores it in Amazon S3.
%% @end
%%-------------------------------------------------------------------------
create_snapshot(AWSInfo,VolumeId) ->
	Query = [{'Action',"CreateSnapshot"},{'VolumeId',VolumeId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,AvailabilityZone,Size::integer()) -> any()
%% @doc  Creates a new Amazon EBS volume that you can mount from any Amazon EC2 instance.
%% @end
%%-------------------------------------------------------------------------
create_volume(AWSInfo,AvailabilityZone,Size) when is_integer(Size) ->
	Query = [{'Action',"CreateVolume"},{'AvailabilityZone',AvailabilityZone},{'Size',Size}],
	command(AWSInfo,Query);
%%-------------------------------------------------------------------------
%% @spec (AWSInfo,AvailabilityZone,SnapshotId::list()) -> any()
%% @doc  Creates a new Amazon EBS volume that you can mount from any Amazon EC2 instance.
%% @end
%%-------------------------------------------------------------------------
create_volume(AWSInfo,AvailabilityZone,SnapshotId) when is_list(SnapshotId) ->
	Query = [{'Action',"CreateVolume"},{'AvailabilityZone',AvailabilityZone},{'SnapshotId',SnapshotId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,KeyName) -> any()
%% @doc  Deletes a keypair.
%% @end
%%-------------------------------------------------------------------------
delete_key_pair(AWSInfo,KeyName) ->
	Query = [{'Action',"DeleteKeyPair"},{'KeyName',KeyName}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName) -> any()
%% @doc  Deletes a security group.
%% @end
%%-------------------------------------------------------------------------
delete_security_group(AWSInfo,GroupName) ->
	Query = [{'Action',"DeleteSecurityGroup"},{'GroupName',GroupName}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,SnapshotId) -> any()
%% @doc  Deletes a snapshot of an Amazon EBS volume that is stored in Amazon S3.
%% @end
%%-------------------------------------------------------------------------
delete_snapshot(AWSInfo,SnapshotId) ->
	Query = [{'Action',"DeleteSnapshot"},{'SnapshotId',SnapshotId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId) -> any()
%% @doc  Deletes an Amazon EBS volume. For more information about Amazon EBS.
%% @end
%%-------------------------------------------------------------------------
delete_volume(AWSInfo,VolumeId) ->
	Query = [{'Action',"DeleteVolume"},{'VolumeId',VolumeId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId) -> any()
%% @doc  Detaches an Amazon EBS volume from an instance. For more information about Amazon EBS.
%% @end
%%-------------------------------------------------------------------------
detach_volume(AWSInfo,VolumeId) -> detach_volume(AWSInfo,VolumeId,[],[],[]).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId,InstanceId,Device,Force) -> any()
%% @doc  Detaches an Amazon EBS volume from an instance. For more information about Amazon EBS.
%% @end
%%-------------------------------------------------------------------------
detach_volume(AWSInfo,VolumeId,InstanceId,Device,Force) ->
	Query = [{'Action',"DetachVolume"},{'VolumeId',VolumeId},{'InstanceId',InstanceId},{'Device',Device},{'Force',Force}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageId) -> any()
%% @doc  Operation deregisters an AMI. Once deregistered, 
%%       instances of the AMI may no longer be launched.
%% @end
%%-------------------------------------------------------------------------
deregister_image(AWSInfo,ImageId) ->
	Query = [{'Action',"DeregisterImage"},{'ImageId',ImageId}],
	command(AWSInfo,Query).		

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,PublicIpList) -> any()
%% @doc  Lists elastic IP addresses assigned to your account.
%% @end
%%-------------------------------------------------------------------------
describe_addresses(AWSInfo,PublicIpList) ->
	IPList = eifaws_util:param_list('PublicIp',PublicIpList),
	Query = lists:append([{'Action',"DescribeAddress"}],IPList),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ZoneName) -> any()
%% @doc  Describes availability zones that are currently available to the account and their states.
%% @end
%%-------------------------------------------------------------------------
describe_availability_zones(AWSInfo,ZoneList) ->
	Zones = eifaws_util:param_list('ZoneName',ZoneList),
	Query = lists:append([{'Action',"DescribeAvailabilityZones"}],Zones),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageId,Attribute) -> any()
%% @doc  Operation returns information about an attribute of an AMI.
%% @end
%%-------------------------------------------------------------------------
describe_image_attribute(AWSInfo,ImageId,Attribute) ->
	Query = [{'Action',"DescribeImageAttributes"},{'ImageId',ImageId},{'Attribute',Attribute}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageIdList,OwnerList,ExecutableByList) -> any()
%% @doc  Returns information about AMIs, AKIs, and ARIs available to the user.
%% @end
%%-------------------------------------------------------------------------
describe_images(AWSInfo,ImageIdList,OwnerList,ExecutableByList) ->
	Images = eifaws_util:param_list('ImageId',ImageIdList),
	Owners = eifaws_util:param_list('Owner',OwnerList),
	Execs  = eifaws_util:param_list('ExecutableBy',ExecutableByList),
	Query = lists:append([[{'Action',"DescribeImages"}],Images,Owners,Execs]),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo) -> any()
%% @doc  Operation returns information about instances owned by the user making the request.
%% @end
%%-------------------------------------------------------------------------
describe_instances(AWSInfo) -> describe_instances(AWSInfo,[]).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,InstanceIdList::list()) -> any()
%% @doc  Operation returns information about instances owned by the user making the request.
%% @end
%%-------------------------------------------------------------------------
describe_instances(AWSInfo,InstanceIdList) ->
	InstanceIds = build_list("InstanceId",InstanceIdList),
	Query = lists:append([{'Action',"DescribeInstances"}],InstanceIds),
	command(AWSInfo,Query).


%%-------------------------------------------------------------------------
%% @spec (AWSInfo) -> any()
%% @doc  Returns information about key pairs available to you.
%% @end
%%-------------------------------------------------------------------------
describe_key_pairs(AWSInfo) -> describe_key_pairs(AWSInfo,[]).
%%-------------------------------------------------------------------------
%% @spec (AWSInfo,KeyName) -> any()
%% @doc  Returns information about key pairs available to you.
%% @end
%%-------------------------------------------------------------------------
describe_key_pairs(AWSInfo,KeyNameList) ->
	KeyNames = build_list("KeyName",KeyNameList),
	Query = lists:append([{'Action',"DescribeKeyPairs"}],KeyNames),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName) -> any()
%% @doc  Returns information about security groups that you own.
%% @end
%%-------------------------------------------------------------------------
describe_security_groups(AWSInfo,GroupNameList) ->
	GroupNames = build_list("GroupName",GroupNameList),
	Query = lists:append([{'Action',"DescribeSecurityGroups"}],GroupNames),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,SnapshotId) -> any()
%% @doc  Describes the status of Amazon EBS snapshots.
%% @end
%%-------------------------------------------------------------------------
describe_snapshots(AWSInfo,SnapshotIdList) ->
	SnapshotIds = build_list("SnapshotId",SnapshotIdList),
	Query = lists:append([{'Action',"DescribeSnapshots"}],SnapshotIds),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,VolumeId) -> any()
%% @doc  lists one or more Amazon EBS volumes that you own.
%% @end
%%-------------------------------------------------------------------------
describe_volumes(AWSInfo,VolumeIdList) ->
	VolumeIds = build_list("VolumeId",VolumeIdList),
	Query = lists:append([{'Action',"DescribeVolumes"}],VolumeIds),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,PublicIp) -> any()
%% @doc  Disassociates the specified elastic IP address from the instance to which it is assigned.
%% @end
%%-------------------------------------------------------------------------
disassociate_address(AWSInfo,PublicIp) ->
	Query = [{'Action',"DisassociateAddress"},{'PublicIp',PublicIp}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,InstanceId) -> any()
%% @doc  Retrieves console output for the specified instance.
%% @end
%%-------------------------------------------------------------------------
get_console_output(AWSInfo,InstanceId) ->
	Query = [{'Action',"GetConsoleOutput"},{'InstanceId',InstanceId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageAttricutes) -> any()
%% @doc  Modifies an attribute of an AMI.
%% @end
%%-------------------------------------------------------------------------

modify_image_attribute(AWSInfo,#image_attributes{attribute = 'launchPermission'} = ImageAttributes) -> 
	Users = build_list("UserId",ImageAttributes#image_attributes.users),
	Groups = build_list("UserGroup",ImageAttributes#image_attributes.groups),
	Query = lists:append([[
			{'Action',"ModifyImageAttribute"},
			{'ImageId',ImageAttributes#image_attributes.image_id},
			{'Attribute',ImageAttributes#image_attributes.attribute},
			{'OperationType',ImageAttributes#image_attributes.operation_type}],
			Users,Groups]),
	command(AWSInfo,Query);
modify_image_attribute(AWSInfo,#image_attributes{attribute = 'productCodes'} = ImageAttributes) -> 
	ProductCode = build_list("ProductCode",ImageAttributes#image_attributes.product_code),
	Query = lists:append([[
			{'Action',"ModifyImageAttribute"},
			{'ImageId',ImageAttributes#image_attributes.image_id},
			{'Attribute',ImageAttributes#image_attributes.attribute}],
			ProductCode]),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,InstanceIdList) -> any()
%% @doc  Requests a reboot of one or more instances.
%% @end
%%-------------------------------------------------------------------------
reboot_instances(AWSInfo,InstanceIdList) ->
	InstanceIds = build_list("InstanceId",InstanceIdList),
	Query = lists:append([{'Action',"RebootInstances"}],InstanceIds),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,PublicIp) -> any()
%% @doc  Releases an elastic IP address associated with your account.
%% @end
%%-------------------------------------------------------------------------
release_address(AWSInfo,PublicIp) ->
	Query = [{'Action',"ReleaseAddress"},{'PublicIp',PublicIp}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageLocation) -> any()
%% @doc  Operation registers an AMI with Amazon EC2.
%% @end
%%-------------------------------------------------------------------------
register_image(AWSInfo,ImageLocation) ->
	Query = [{'Action',"RegisterImage"},{'ImageLocation',ImageLocation}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,ImageId,Attribute) -> any()
%% @doc  Resets an attribute of an AMI to its default value.
%% @end
%%-------------------------------------------------------------------------
reset_image_attribute(AWSInfo,ImageId,Attribute) ->
	Query = [{'Action',"ResetImageAttribute"},{'ImageId',ImageId},{'Attribute',Attribute}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName,SourcesSecurityGroupName,SourcesSecurityGroupOwnerId) -> any()
%% @doc  Adds permissions to a security group.
%% @end
%%-------------------------------------------------------------------------
revoke_security_group_ingress(AWSInfo,GroupName,SourceSecurityGroupName,SourceSecurityGroupOwnerId) ->
	Query = [{'Action',"RevokeSecurityGroupIngress"},{'GroupName',GroupName},{'SourceSecurityGroupName',SourceSecurityGroupName},{'SourceSecurityGroupOwnerId',SourceSecurityGroupOwnerId}],
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,GroupName,IpProtocol,FromPort,ToPort,CidrIp) -> any()
%% @doc  Adds permissions to a security group.
%% @end
%%-------------------------------------------------------------------------
revoke_security_group_ingress(AWSInfo,GroupName,IpProtocol,FromPort,ToPort,CidrIp) ->
	Query = [{'Action',"RevokeSecurityGroupIngress"},{'GroupName',GroupName},{'IpProtocol',IpProtocol},{'FromPort',FromPort},{'ToPort',ToPort},{'CidrIp',CidrIp}],
	command(AWSInfo,Query).


%%-------------------------------------------------------------------------
%% @spec (AWSInfo,RunInstance::record()) -> any()
%% @doc  Launches a specified number of instances.
%% @end
%%-------------------------------------------------------------------------
run_instances(AWSInfo,ImageId) when is_list(ImageId) -> run_instances(AWSInfo,#run_instance{image_id=ImageId});
run_instances(AWSInfo,RunInstance) when is_record(RunInstance,run_instance) ->
	InstanceQuery = build_run_instances(RunInstance),
	Query = lists:append([{'Action',"RunInstances"}],InstanceQuery),
	command(AWSInfo,Query).

%%-------------------------------------------------------------------------
%% @spec (AWSInfo,InstanceIds::list()) -> any()
%% @doc  Operation shuts down one or more instances.
%% @end
%%-------------------------------------------------------------------------
terminate_instances(AWSInfo,InstanceIdList) ->
	InstanceIds = build_list("InstanceId",InstanceIdList),
	Query = lists:append([{'Action',"TerminateInstances"}],InstanceIds),
	command(AWSInfo,Query).






%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% MetaData Funcations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

meta_data(MetaData) -> meta_data(MetaData,"latest").

meta_data(ami_id,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/ami-id",
	get_meta_data(MetaDataURL);
meta_data(ami_launch_index,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/ami-launch-index",
	get_meta_data(MetaDataURL);
meta_data(ancestor_manifest_path,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/ancestor-manifest-path",
	get_meta_data(MetaDataURL);
meta_data(block_device_mapping,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/block-device-mapping",
	get_meta_data(MetaDataURL);
meta_data(instance_id,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/instance-id",
	get_meta_data(MetaDataURL);
meta_data(instance_type,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/instance-type",
	get_meta_data(MetaDataURL);
meta_data(local_hostname,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/local-hostname",
	get_meta_data(MetaDataURL);
meta_data(local_ipv4,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/local-ipv4",
	get_meta_data(MetaDataURL);
meta_data(kernel_id,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/kernel-id",
	get_meta_data(MetaDataURL);
meta_data(placement,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/placement",
	get_meta_data(MetaDataURL);
meta_data(product_codes,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/product-codes",
	get_meta_data(MetaDataURL);
meta_data(public_hostname,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/public-hostname",
	get_meta_data(MetaDataURL);
meta_data(public_ipv4,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/public-ip4v",
	get_meta_data(MetaDataURL);
meta_data(public_keys,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/public_keys/",
	get_meta_data(MetaDataURL);
meta_data(ramdisk_id,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/ramdisk-id",
	get_meta_data(MetaDataURL);
meta_data(reservation_id,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/reservation-id",
	get_meta_data(MetaDataURL);
meta_data(security_groups,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/meta-data/security-groups",
	get_meta_data(MetaDataURL);

meta_data(user_data,Version) ->
	MetaDataURL = ?AWS_EC2_META_DATA_BASE_URL ++ Version ++ "/user-data",
	get_meta_data(MetaDataURL).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Internal Funcations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



command(Atom,Q) when is_atom(Atom) -> command(eifaws_util:get_account(Atom),Q);
command(AWSInfo,Q) when is_record(AWSInfo,aws_info) -> 
	Query = check_query(AWSInfo,Q),
	QueryURL = eifaws_util:get_server(ec2) ++ build_query(AWSInfo,Query),
	?D(QueryURL),
	?DUMP("/sfe/ec2/samples/query.txt",QueryURL),
	Module   = version_module(get_query_param('Version',Query)),
	case eifaws_util:request(QueryURL) of
		{ok,Body} -> 
			Action = eifaws_util:to_atom(get_query_param('Action',Query)),
			Module:ec2(Action,Body);
		{error,Reason,Code,Message} -> parse_error(Reason,Code,Message);
		{error,Reason} -> {error,Reason}
	end.

get_meta_data(MetaDataURL) ->
	case eifaws_util:request(MetaDataURL) of
		{ok,Body} -> Body;
		{error,Reason} -> {error,Reason}
	end.	



version_module(Version) ->
	case regexp:gsub(Version,"-","_") of
		{ok,String,_} -> list_to_atom("eifaws_ec2_" ++ String);
		_ -> error
	end.

get_query_param(Key,Query) ->
	case lists:keysearch(Key,1,Query) of
		{value,{Key,Version}} -> Version;
		_ -> ?CURRENT_VERSION_EC2
	end.

build_query(AWSInfo,Query) when is_record(AWSInfo,aws_info) -> build_query(AWSInfo#aws_info.aws_secret_key_id,Query);
build_query(SecretKey,Query) -> 
	Sorted = eifaws_ec2:query_sort(Query),
	{QS,SS} = lists:foldl(fun({Action,Value},{Q,S}) ->
			ActionString = atom_to_list(Action),
			case {Action,Value} of
				{'Timestamp',Value} when is_tuple(Value) ->
					V = eifaws_util:iso_8601_fmt(Value),
					{[["&",ActionString,"=",eifaws_util:escape(V)]|Q],[[ActionString,V]|S]};
				{_, Value} when is_integer(Value) -> 
					V = integer_to_list(Value),
					{[["&",ActionString,"=",eifaws_util:escape(V)]|Q],[[ActionString,V]|S]};
				{_, Value} when is_atom(Value) -> 
					V = atom_to_list(Value),
					{[["&",ActionString,"=",eifaws_util:escape(V)]|Q],[[ActionString,V]|S]};
				{_,V}-> {[["&",ActionString,"=",eifaws_util:escape(V)]|Q],[[ActionString,Value]|S]}
			end
		end, {[],[]}, Sorted),
	SString = lists:flatten(lists:reverse(SS)),
	Sig = binary_to_list(crypto:sha_mac(SecretKey,SString)),
	B64Sig = binary_to_list(base64:encode(Sig)),
	{ok,QString,_} = regexp:sub(lists:flatten(lists:reverse(QS)),"&","?"),
	QString++ "&Signature=" ++ eifaws_util:escape(B64Sig).

check_query(AWSInfo,Q) -> check_query(AWSInfo,Q,['AWSAccessKeyId','Timestamp','Version','SignatureVersion']).
check_query(_AWSInfo,Q,[]) -> query_sort(Q);
check_query(AWSInfo,Q,[H|T]) ->
	case lists:keymember(H,1,Q) of
		true -> check_query(AWSInfo,Q,T);
		false ->
			case H of
				'AWSAccessKeyId'   -> check_query(AWSInfo,[{'AWSAccessKeyId',AWSInfo#aws_info.aws_access_key}|Q],T);
				'Timestamp'        -> check_query(AWSInfo,[{'Timestamp',calendar:universal_time()}|Q],T);
				'Version'          -> check_query(AWSInfo,[{'Version',?CURRENT_VERSION_EC2}|Q],T);
				'SignatureVersion' -> check_query(AWSInfo,[{'SignatureVersion',1}|Q],T);
				_ -> Q
			end
	end.

query_sort(QueryList)  ->
	{_Blanks,CleanQueryList} = lists:partition(fun({_K,V}) ->
			V == []
		end, QueryList),
	lists:sort(fun({Key1,_V1},{Key2,_V2}) ->
	S1 = string:to_lower(atom_to_list(Key1)),
	S2 = string:to_lower(atom_to_list(Key2)),
	if
		S1 < S2 -> true;
		true -> false
	end 
	end,lists:flatten(CleanQueryList)).






build_run_instances(RunInstances) when is_record(RunInstances,run_instance) -> build_run_instances(RunInstances,[image_id,min_count,max_count,key_name,instance_type],[]).

build_run_instances(_RI,[],Acc) -> Acc;

build_run_instances(RI,[image_id|T],Acc) when RI#run_instance.image_id /= []->
	build_run_instances(RI,T,[{'ImageId', RI#run_instance.image_id}|Acc]);
build_run_instances(RI,[min_count|T],Acc) when RI#run_instance.min_count /= [] ->
	build_run_instances(RI,T,[{'MinCount', RI#run_instance.min_count}|Acc]);
build_run_instances(RI,[max_count|T],Acc) when RI#run_instance.max_count /= [] ->
	build_run_instances(RI,T,[{'MaxCount', RI#run_instance.max_count}|Acc]);
build_run_instances(RI,[key_name|T],Acc) when RI#run_instance.key_name /= [] ->
	build_run_instances(RI,T,[{'KeyName', RI#run_instance.key_name}|Acc]);
build_run_instances(RI,[instance_type|T],Acc) when RI#run_instance.instance_type /= [] ->
	build_run_instances(RI,T,[{'InstanceType', RI#run_instance.instance_type}|Acc]);

build_run_instances(RI,[_H|T],Acc) ->
	build_run_instances(RI,T,Acc).




build_list(String,List) -> build_list(String,[],List,1,[]).
build_list(String,Post,List) -> build_list(String,Post,List,1,[]).

build_list(_Pre,_Post,[],_Count,Acc) -> Acc;
build_list(Pre,Post,[H|T],Count,Acc) ->
	Item = case H of
		H when is_integer(H) -> {list_to_atom(Pre ++ "." ++ integer_to_list(Count) ++ Post),integer_to_list(H)};
		H when is_atom(H)    -> {list_to_atom(Pre ++ "." ++ integer_to_list(Count) ++ Post),atom_to_list(H)};
		H when is_list(H)    -> {list_to_atom(Pre ++ "." ++ integer_to_list(Count) ++ Post),H}
	end,
	build_list(Pre,Post,T,Count+1,[Item|Acc]).





parse_error(Type,HttpCode,ErrorMessage) ->
	XMLRoot = eifaws_util:to_xml(ErrorMessage),
	RequestId = eifaws_util:xml_value(xmerl_xpath:string("RequestID",XMLRoot)),
	Errors = xmerl_xpath:string("Errors/Error",XMLRoot),
	lists:map(fun(E) ->
		Code = eifaws_util:xml_value(xmerl_xpath:string("Code",E)),
		Message = eifaws_util:xml_value(xmerl_xpath:string("Message",E)),
		#ec2_error{type=Type,http_code=HttpCode,code=Code,message=Message,request_id=RequestId}
	end,Errors).





